Note: The Apple Remote Access API provides an application programming interface to the Remote Access Manager. It supports calls to load and unload the Remote Access Manager (RAM), create and terminate connections, retrieve the current RAM status, and to determine if a specified network address is local or remote. Optionally, the Apple Remote Access API can display a user interface showing the process of a connection. The parameters for the connect call can be a connection document, created with the Remote Access application. The parameters of the connection, however, may not be specified out right when connecting to ARA 2.0. These specifications also include how to tell if Apple Remote Access is installed, and how to deal with the serial port when Apple Remote Access is in answer mode.
Although every attempt has been made to verify the accuracy of the information presented, this document may contain errors and is subject to change. ARA 2.0 features are not guaranteed.
Gestalts
When fully installed, Remote Access defines several new gestalt selectors. Below are the new defines and explanation of their use.
To see if serial port arbitration is installed, call _Gestalt using these defines.
#define gestaltArbitorAttr 'arb '
#define gestaltSerialArbitrationExists 0
For example:
OSErr io;
// check to see if serial port arbitrating is installed…
More information on serial port arbitration is discussed in a section below.
To see if Remote Access Connection Interface is available use these defines:
#define gestaltRemoteAccessAttr 'strm'
#define gestaltRemoteAccessExists 0
To check if Remote Access support is available in the Alias Manager use these defines:
#define gestaltAliasMgrAttr 'alis' // as defined in GestaltEqu.h
#define gestaltAliasMgrSupportsRemoteAppletalk 1
To use ARA 2.0 features, call _Gestalt using these defines.
#define gestaltRemoteAccessCallOnly 1 // checks for ARA client
#define gestaltRemoteAccessMPServer 2 // checks for ARA multi-port server
#define gestaltRemoteAccessVers2 3 // checks for ARA 2.0 features
Serial Port Arbitration
When installed Remote Access provides serial port arbitration throught the Serial Port Arbitrator tool. All serial drivers registered with the Communications Resource Manager are arbitrated by the Serial Port Arbitrator.
To check to see if the Serial Port Arbitrator is installed, check the gestaltSerialArbitrationExists flag of the gestaltArbitorAttr Gestalt selector (see “Gestalts” section for these defines and an example of this call).
If serial port arbitration is present, call OpenDriver when you want to use a serial port. If the driver requested is not open, OpenDriver will return a result of noErr and the reference number of the driver. If the driver requested is open (in use), OpenDriver will return the error portInUse. When you are finished using the driver, call CloseDriver. OpenDriver and CloseDriver calls should always be balanced, although the Serial Port Arbitrator will protect against multiple open/close calls.
If serial port arbitration is not present, do not use OpenDriver to determine if the driver is open. It will return you a result of noErr and the reference number, allowing you access to a driver that another application is using. To determine if the serial driver requested is open by another application, you must walk the DCE (Device Control Entry) unit table (see Device Driver chapter of Inside Macintosh vol II).
It is important to use the new method if serial port arbitration is available. Remote Access, when set up for answering calls, is passively using a particular serial driver. If you use the new method, the Serial Port Arbitrator will give up the passive claim and allow your OpenDriver call to return noErr. Later, when you call CloseDriver, Remote Access will again passively claim the port and setup the modem for answering.
Below is a code example in C illustrating how to use serial port drivers under the new and old methods:
The TRemoteAccessParamBlock is a union of all of the available Apple Remote Access API commands. The TRemoteAccessParmHeader is a struct which consists of a DControlParamHeader followed by a DExtendedParam which is followed by a DRemoteAccessParmHeader. The extendedCode is used to specify the Apple Remote Access API command wanted. The resultStrPtr field returns a Pascal string to indicate what error occurred. If you are not interested in the string, set this field to nil. If you do pass a pointer however, it must point to a buffer of at least 256 bytes in length. If the result of the call happens to be noErr, then the length byte of the string will be zero. Since this version of Remote Access only deals with the user port, the parameter portGlobalsPtr should always be set to zero. The csCode field should normally set to RAM_EXTENDED_CALL and the extendedType is set to REMOTEACCESSNAME. These constants are defined in RemoteAccessInterface.h.
#define DControlParamHeader \
QElem *qLink; // next queue entry \
short qType; // queue type \
short ioTrap; // routine trap \
Ptr ioCmdAddr; // routine address \
ProcPtr ioCompletion; // completion routine \
OSErr ioResult; // result code \
long userData; // for use by the user \
short unused; // unused field \
short ioRefNum; // driver reference number \
short csCode; // normally set to RAM_EXTENDED_CALL
// for Apple Remote Access API calls
#define DExtendedParam \
DControlParamHeader \
Ptr hReserved1; \
Ptr hReserved2; \
Ptr resultStrPtr; \ // set to zero if result string is unwanted
Ptr extendedType; // pointer to identifier string, normally set to // REMOTEACCESSNAME for Apple Remote Access API calls
#define DRemoteAccessParmHeader \
DExtendedParam \
short extendedCode; // for use by extended call proc \
Ptr portGlobalsPtr; // pointer to globals for this port (0=userport) \
The load command is used to ensure that the Remote Access Manager is loaded into memory and must be used before making a Connect call . It uses the standard TRemoteAccessParmBlock as the parameter block. The example code below shows how it works. To use the MungePW command, it is not necessary to use the load command first.
The unload command is used to release the Remote Access Manager and free its memory. It uses the standard TRemoteAccessParmBlock as the parameter block and can be issued immediately after making a Connect call. This allows the Remote Access Manager to be unloaded as soon as an active connection is terminated with a disconnect, if no other clients have loaded Remote Access. Below is an example unload call.
#include “RemoteAccessInterface.h”
void UnloadRemoteAccess()
{
TRemoteAccessParmBlock unloadPB;
// unload the code (will not actually go away till this connection is done)
This call is used to initiate an outgoing connection. When you are connected in this mode you will still retain access to your current network. Network numbers are re-mapped in a limited way in order to solve problems of network number conflicts between the two machines being directly connected, thus ensuring they will always be accessible to each other. Unfortunately, it is not possible to solve all of the other possible conflicts due to the limited number of network numbers available. In order to provide a method of ensuring access to all networks on the destination network a guaranteed access method is available. When connected in this mode, you will lose access to all services beyond those on the same single network number that the calling machine belongs to. In order to notify clients of the AppleTalk stack that a network will no longer be reachable we have created a new AppleTalk Transition Queue event. (See Network Transition Events later in this document.) When connecting the client passes in a TRAConnectInfoTemplate, or the FSSpec of a document which contains the connect parameters. The connect parameter block contains the optionFlags field which specifies the connect options. The flags are shown below:
// connect option flags
#define kNSCanInteract 0x00000001 // User interaction (password prompt) is OK
#define kNSShowStatus 0x00000002 // show the status of the connect or disconnect call
#define kNSConnectDocument 0x00000004 // connect using the specified document using FSSpec
#define kNSPassWordSet 0x00000010 // use the specified password field when connecting // by document
// 2.0 and above…
#define kNS2SavvyFlags 0x40000000 // Set to use the next 2 flags below for only ARA // 2.0 aware applications.
#define kNSAR2Connection 0x00000020 // connecting to a 2.0 server.
#define kNSNotifyWhileConnected 0x00000040 // display cute notification icon while connected.
The kNSCanInteract flag allows interaction with the user to get the password if necessary or prompt the user to change their password if the server requires it. The kNSShowStatus flag enables the connection status display. The display is modal dialog which updates with new messages as the connection progresses. When the kNSConnectDocument flag is set, the Apple Remote Access API will use the specified ARA 2.0 document for the connect parameters (i.e. user name, password) of the TRAConnectInfoTemplate. The document is specified by the FSSpec record which contains vRefNum (volume reference number), parID (directory ID), and name (pointer to Pascal style string containing the document name). No other parameters need to be supplied. The kNSPassWordSet flag overrides the saved password when connecting by document or by PB and forces Apple Remote Access API to use the passWord field in cleartext. If the kNSPassWordSet flag is clear and the passwordSaved flag is set, then the client must supply a munged password. The kNS2SavvyFlags is set if using ARA 2.0 features. The kNSAR2Connection flag indicates the connection is to a 2.0 server. If this bit is not set, it is assumed that it is a 1.0 connection. The kNSNotifyWhileConnected flag will display a notification icon on the Apple Menu while the user is connected.
In this release, the client may not connect by parameter block (PB). Use the kNSConnectDocument flag to connect with a specified ARA 2.0 document. The fields in the connectInfo record are discussed below. They are filled by the specified ARA 2.0 document. Within this record is a version which is used by the connect template (currently set to 1). The ltType parameter specifies the type of the link tool that will be used in this connection. The ARA 2.0 connect document specifies the length and points to the address used in connecting by setting up addressInfoLength and addressInfoPtr. The ltSpecificTemplatePtr is expected to point to the template of the link tool specific parms. An example of link tool specific parms might be items such as the serial port reference (.AIn, .AOut) that is used in the Modem Link Tool. A userName is passed in that indicates the name of the user logging in. A passWord is specified if the user is not logging in as a guest. If the user wants to be a guest, the guestLogin flag is set. The connectReminderTimer is used if the caller wants to be reminded that a connection is in progress. This field is set to the number of seconds between reminders, and can be set to zero if no reminders are wanted. If a connectReminderTimer is set you must set the connectOKWaitTimer that indicates how long the reminder dialog will wait for OK to be hit before disconnecting.
struct TRAConnectInfoTemplate
{
unsigned long version; // version of this format
unsigned long ltType; // Link Tool type
long addressInfoLength; // length of the address information
Ptr addressInfoPtr; // pointer to connect address info
long ltSpecificTemplateLength; // length of the ltspecific information
Ptr ltSpecificTemplatePtr; // pointer to link tool specific params
unsigned char passWord[PASSWORDBUFSIZE]; // user password
unsigned char userName[USERNAMESIZE]; // user name
unsigned long connectReminderTimer; // value for connection reminder in seconds
unsigned long connectOKWaitTimer; // how long to wait for OK on reminder timer
Boolean guestLogin; // try to log in as a guest
Boolean passwordSaved; // set if password is saved
Boolean guaranteedAccess; // flag to guarantee access to servers internet
ShowError(connectPB.CONNECT.ioResult); // Do Error reporting and recovery
UnloadRemoteAccess(); // Unload when disconnected.
} // DoConnect
Disconnect
The disconnect command is used to terminate an existing session or cancel one that is being created. If you only want to disconnect a session that was connected with a specific parameter block you can do so by setting a pointer to the parameter block used to issue the connect in abortOnlyThisPB. If you want to disconnect a connection created by anyone, you set the abortOnlyThisPB field to zero. If you are disconnecting an outgoing call, you pass zero in portGlobalsPtr. Disconnecting ports other than the userport, is not supported in this version. You should always set disconnectin to zero. The option kNSShowStatus will cause the Apple Remote Access API to display the status dialog during the disconnect.
#define kNumWarnEntriesMax 5 // number of entries in warn array
struct TRemoteAccessDisconnectParam
{
DRemoteAccessParmHeader
unsigned long disconnectin; // Note: Set this parameter to 0
TPRemoteAccessParamBlock abortOnlyThisPB; // only abort a connection opened by this pb
unsigned long warnArr[kNumWarnEntriesMax]; // set warn times here in seconds (zero all if
// no warnings)
unsigned long optionFlags; // bit mapped connect option flags
ShowError(disconnectPB.DISCONNECT.ioResult); // Do Error reporting and recovery
}
IsRemote
The "IsRemote" command is used to determine if a network address is remote or local. If the network is remote, the call will optionally return the information necessary to make the connection to the remote network. The parameter theAddress contains the network address to be checked. The format of theAddress is the same as for the struct AddrBlock as defined in AppleTalk.h:
Bytes 3 & 2 (High Word): Network Number
Byte 1: Node Number
Byte 0 (Low Byte): Socket Number
The optionFlags parameter is used for getting, or disposing, connection information. The following flags are defined:
#define ctlir_getConnectInfo 0x01 // will get connect info if address remote
#define ctlir_disposeConnectInfo 0x02 // will dispose info in connectInfoPtr properly
If the ctlir_getConnectInfo flag is set, and the network address is remote, the information necessary to create the remote connection is returned. If the ctlir_disposeConnectInfo flag is set, the connect information structure pointed to by connectInfoPtr, is disposed of properly.
The locationIsRemoteFlag parameter is a flag that is returned true if the network address is remote. The connectInfoLength parameter indicates the length of the connect information. The connectInfoPtr is a pointer to the connection information for connecting with the remote address. These values are returned when the network address is remote, and the ctlir_getConnectInfo flag is set.
struct TRemoteAccessIsRemoteParms
{
DRemoteAccessParmHeader
long theAddress; // address that is to be checked
unsigned long optionFlags; // Set to ctlir_getConnectInfo or
// ctlir_disposeConnectInfo, if zero only checks
// theAddress
Boolean locationIsRemoteFlag; // returns true if address is remote
long connectInfoLength; // length of the following data
TPRAConnectInfoTemplate connectInfoPtr; // The connection information template pointer
The status command is used to obtain information about Remote Access. The information you can obtain is how long a connection has been active, how much time remains in the connection, the name of the user that made an answering connection, the name of the computer you are connected to on a calling connection, and the last message that was posted. The statusBits parameter is used to determine if a connection is active, starting up, in the process of tearing down, if the connection is an answering or calling connection, if the computer is enabled to receive answer calls or if a disconnect is in progress. The following flags are defined:
// bits passed back in statusBits
#define CctlConnected 0x00000001 // set when connected
#define CctlAnswerEnable 0x00000004 // set when we are set to answer calls
#define CctlServerMode 0x00000008 // set for answer mode, clear for call mode
#define CctlConnectionAborting 0x00000010 // connection is being torn down
#define CctlConnectInProg 0x00000020 // set when connection in progress or fully
// connected
#define CctlDisconnectInStarted 0x00008000 // somebody has started a disconnectIn
#define ctlAR2Connection 0x02000000 // set when this connection is to a 2.0 server
#define ctlNotifyWhileConnected 0x04000000 // set when the user wants to be reminded while // connected.
#define ctlConnectedToMPS 0x08000000 // set when client is connected to a multi-port // server
#define CctlMultiNodeReady 0x80000000 // shows if we currently have a multinode // address to enable answer mode.
The following struct is used when making a status call:
struct TRemoteAccessStatusParam
{
DRemoteAccessParmHeader
unsigned long statusBits; // bits for current status
unsigned long timeConnected; // number of seconds we have been connected
unsigned long timeLeft; // number of seconds remaining in connection
// (0xffffffff infinite)
unsigned char *userNamePtr; // returns user name, expects pointer to buffer // of USERNAMESIZE if non nil
unsigned char *connectedToNamePtr; // returns name of where we connected to, // expects pointer to buffer of USERNAMESIZE if // non nil
TPRemoteAccessParamBlock connectedByParamPtr; // a pointer to the parameter block
// "initiating" the connection if we are // connected
TPRemoteAccessParamBlock statusConnectedByParamPtr; // a pointer to the parameter block
// "initiating" the connection when status was
// posted
unsigned char *theLastStatusMsgPtr; // expects pointer to buffer of size // MAXSTATUSMSGSIZE
unsigned char *statusUserNamePtr; // pointer to buffer of size USERNAMESIZE
long statuslttype; // link tool type
long statusmsgOptionFlags; // classification of message type
long statusMsgNum; // specific message number
long statusMsgSeqNum; // pass in zero if always want status, otherwise // use last value, if status is new, new number
// is returned
unsigned long userSignature; // signature of port creator
unsigned long userRefCon; // refcon of port creator
ShowError(statusPB.STATUS.ioResult); // Do Error reporting and recovery
else
{
// now decode the flag bits into words
statusBits = statusPB.STATUS.statusBits;
if (statusBits & CctlServerMode)
printf("Answer connection\n");
if (statusBits & CctlConnected)
printf("Calling connection\n");
if (statusBits & CctlConnectionAborting)
printf("Cancel in progress\n");
if (statusBits & CctlAnswerEnable)
printf("Waiting for incoming call\n");
if (statusBits & CctlConnectInProg)
printf("Connection in progress\n");
}
} // GetStatus
GetUserPortGlobalsPtr
In order to make the RAM Status call on a machine that is setup to answer calls, you must first make the GetUserPortGlobalsPtr call to retrieve a pointer to the globals for the user port.
The Apple Remote Access (ARA) Personal software supports dial-out and answering capabilities through a single port called the “user” port (the modem or printer port on your Mac). This means that when you setup your machine to answer calls, you can answer only one call at a time on the user port. However, the underlying ARA architecture was designed so that multiple ports may be supported (in a dial-in server for example).
When you dial-out on your Mac and establish an ARA connection, ARA internally allocates the data structures for the user port - but not until a connection is actually made. This is why, for example, the Status call will return the -5833 ERR_PORTDOESNOTEXIST error on the originating machine if there is no active connection. Once a connection has been made on a machine that is the originator of the connection, the Status call should return no error, because the data structures for the port will have been created.
When you make the Status call on a machine that is setup to answer calls (the answer calls box is checked in the Remote Access Setup control panel), you will find that you always get the -5833 ERR_PORTDOESNOTEXIST error. The reason for this is that when ARA is in answer mode it needs to be able to uniquely identify each connection with a separate portGlobalsPtr, because in the future there may be support for more than one connection on a single machine. Therefore, on a machine that is setup to answer calls, you must first retrieve the portGlobalsPtr for the user port before the Status call can be made. This is accomplished with the GetUserPortGlobalsPtr call. This call makes use of the familiar TRemoteAccessParmHeader structure. The pointer to the port globals for the user port is returned in the portGlobalsPtr field upon completion of the call. Here are the data structures used by this call:
The fields in the TRemoteAccessParmHeader structure used for the GetUserPortGlobalsPtr call to the Remote Access Manager are defined as follows:
-> 12 ioCompletion long pointer to completion routine
<- 16 ioResult word result code
-> 20 userData long for use by the user
-> 26 ioRefNum word driver reference number
-> 28 csCode word call command code
-> 40 extendedType long pointer to identifier string
-> 42 extendedCode word for use by extended call procedure
<-> 44 portGlobalsPtr long pointer to globals for this port (0 = return user port globals)
Here are the detailed descriptions of the parameter block fields used by this call:
ioCompletion pointer to completion routine.
ioResult result code returned by the call.
userData user data for use by the user.
ioRefNum driver reference number.
csCode command code, normally set to RAM_EXTENDED_CALL.
extendedType should be set to REMOTEACCESSNAME.
extendedCode set to 54 to indicate the GetUserPortGlobalsPtr call.
portGlobalsPtr returns a pointer to the globals for the port. On input pass 0 to indicate you want the user port globals.
The following result codes can be returned by the GetUserPortGlobalsPtr call:
noErr 0 no error.
ERR_PORTDOESNOTEXIST -5833 port does not exist.
ERR_PORTSHUTDOWN -5832 port is shutting down.
The following is an example of how you would use the GetUserPortGlobalsPtr call prior to making a Status call.
pb.HDR.extendedType = (Ptr)REMOTEACCESSNAME; /* to Netshare */
pb.HDR.extendedCode = CmdRemoteAccess_GetUserPortGlobalsPtr; /* get user port globals */
pb.HDR.portGlobalsPtr = nil; /* 0 = return user port globals */
PBRemoteAccess( &pb, false );
if (pb.HDR.ioResult != noErr)
HandleError(pb.HDR.ioResult);
else
{
/* Issue ARA Status Call */
ResultStr[0] = 0;
pb.STATUS.resultStrPtr = (Ptr)ResultStr; /* put results here */
pb.STATUS.extendedCode = CmdRemoteAccess_Status; /* status command */
pb.STATUS.userNamePtr = UserName;
pb.STATUS.connectedToNamePtr = ConnectedTo;
pb.STATUS.theLastStatusMsgPtr = LastMessage;
pb.STATUS.statusUserNamePtr = 0;
pb.STATUS.statusMsgSeqNum = 0;
PBRemoteAccess( &pb, false );
if (pb.STATUS.ioResult != noErr)
HandleError(pb.STATUS.ioResult);
}
}
Thus, to make sure that the RAM Status call will work on machines that are setup to answer calls, you will need to first make the GetUserPortGlobalsPtr call so that the Remote Access Manager knows which port to return status information for.
MungePW
The MungePW command is used to encrypt a password to be stored in a document. Normally, when connecting by document, it is not necessary to use this command, since the password in a document is stored in encrypted format. It uses a struct TRemoteAccessPasswordMunger with the inputs username pointer and password pointer. The reserved field should always be set to zero. The munged password is return in the data buffer pointed to by passWordPtr. It is not necessary to call the Load command before using MungePW. The maximum username and password lengths are defined in the RemoteAccessInterface.h header file.
struct TRemoteAccessPasswordMunger
{
DRemoteAccessParmHeader
unsigned char *userNamePtr; // pointer to username string
The GetCodeHooks command is used to return a pointer to the remapper procedure. This routine can then be called to do special remappings for applications passing network addresses as part of their data. The call can be made with the clients network number and the node number and this routine will return the remapped equivalents.
struct TRemoteAccessGetCodeHooks
{
DRemoteAccessParmHeader
RemmaperProcPtr remapperProc; // quick vector to remapper code
The routine returned by this call is defined as follows:
pascal void DoRemapper(unsigned long whereNet, unsigned long incomingFlag, unsigned long sourceSwapFlag, unsigned short *theNet, unsigned char *theNode)
whereNet-> This value has the net where this packet just came from or is going to, it is needed to determine if any remapping should even take place.
incomingFlag-> Set to true if data is incoming, false if data is outgoing.
sourceSwapFlag-> Set to true if a source style swap is to be used. A source style swap means that we do remappings based on the address being a source address. If this flag is false, destination style swaps are done.
theNet-> Pointer to unsigned short containing the net to be remapped.
theNode-> Pointer to unsigned char containing the node to be remapped.
Network transition events are generated by Remote Access to inform interested clients that network connectivity has changed. The type of change is indicated by the newConnectivity flag. If this flag is true, new connectivity is being added (i.e. a connection to a new internet has taken place). In this case, all network addresses will be returned as reachable. If the newConnectivity flag is false, certain networks are no longer reachable. Since Remote Access is connection based and internally functions much like a router it has knowledge of where a specific network exists. Remote Access can take advantage of that knowledge during a disconnect to inform AppleTalk clients that a network is no longer reachable. This information can be used by the AppleTalk client to age out connections immediately rather than waiting a potentially long period of time before discovering that the other end is no longer reachable.
When Remote Access is disconnecting, it will generate a "Network Transition Event (theEvent=5)" through the AppleTalk transition queue. A client upon receiving such a message can ask Remote Access (through a network validate hook passed to the client) if a specific network is still reachable. If the network is still reachable, true will be returned. A client can then continue to check other networks he is interested in until he has learned the status of each of them. After a client is finished checking his networks he returns to Remote Access where the next AppleTalk transition queue client is called.
Since the "Network Transition Event" is transitional, it is important to realize that the information that the network validate hook returns is only valid if a client has just been called as a result of a transition. In other words, a client can only validate networks when it has been called to handle a "Network Transition Event". It is also important to realize that the "Network Transition Event" can be called as the result of an interrupt, so a client should obey all of the normal conventions involved at being called at this time (i.e. don't ask for memory from the memory manager, etc.).
The following information assumes you have already installed yourself into the AppleTalk transition queue.
ATTransNetworkTransition
The ATTransNetworkTransition event will be generated whenever a network transition occurs. You will be passed the following information using C calling conventions:
NetValidProcPtr netValidProc; // pointer to the network valid proc
Boolean newConnectivity; // true=new connectivity, false=loss of connectivity
} TNetworkTransition;
To check a network number for validity the client uses the netValidProc to call Remote Access. This call is defined as follows:
long netValidProc(TNetworkTransition *thetrans, unsigned long theAddress);
thetrans ---> pass in the TNetworkTransition struct given to you when your
transition handler was called.
theAddress ---> this is the network address you want checked. The format of theAddress is the same as for the struct AddrBlock as defined in AppleTalk.h: